using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;

namespace SCIENEW {
    public class CastingTroughDevice : FourDirectionModelDevice, IFluidTransferrerPipeDevice {

        public BlockMesh[] m_portMeshByFaces = new BlockMesh[4];

        public CastingTroughDevice() : base(
            "CastingTroughDevice",
            "Models/CastingTrough",
            "CastingTrough",
            true,
            true
        ) {
            Model model = ContentManager.Get<Model>("Models/CastingTrough");
            Matrix boneAbsoluteTransform = BlockMesh.GetBoneAbsoluteTransform(model.FindMesh("CastingTrough_Port1").ParentBone);
            for (int i = 0; i < 4; i++)
            {
                m_portMeshByFaces[i] = new BlockMesh();
                Matrix identity = Matrix.Identity;
                identity *= Matrix.CreateRotationY(i * (float)Math.PI / 2f) * Matrix.CreateTranslation(0.5f, 0f, 0.5f);
                m_portMeshByFaces[i].AppendModelMeshPart(model.FindMesh("CastingTrough_Port1").MeshParts[0], boneAbsoluteTransform * identity, makeEmissive: false, flipWindingOrder: false, doubleSided: true, flipNormals: false, Color.White);
            }
        }

        public override bool IsTransparent_(int value) => true;

        public override int GetFaceTextureSlot(int face, int value) => 4;

        public override int GetShadowStrength(int value) => 0;

        public override Vector3 GetIconBlockOffset(int value, DrawBlockEnvironmentData environmentData) => new(0, 0.25f, 0);

        public override string GetSoundMaterialName(SubsystemTerrain subsystemTerrain, int value) => "Stone";

        public string GetEntityName(int value) => "CastingTroughDevice";

        public Widget GetWidget(IInventory inventory, ComponentCastingBasin component) => null;

        public FluidTransferrer? CreateFluidTransferrer(SubsystemFluidTransfer subsystemFluidTransfer, SubsystemTerrain subsystemTerrain, int value, Point3 point) => new CastingBasinTransferrer(point, subsystemTerrain);

        public FluidConnector? GetConnector(SubsystemTerrain terrain, int value, int face, int x, int y, int z) {
            if (face == CellFace.OppositeFace(GetDirection(value))) return new FluidConnector(new CellFace(x, y, z, face), FluidConnectorType.Input, 1);
            return null;
        }

        public override void GenerateTerrainVertices(BlockGeometryGenerator generator, TerrainGeometry geometry, int value, int x, int y, int z) {
            generator.GenerateShadedMeshVertices(
                BlocksManager.Blocks[BaseDeviceBlock.Index],
                x,
                y,
                z,
                m_blockMeshesByData[GetDirection(value)],
                Color.White,
                null,
                null,
                geometry.GetGeometry(IndustrialModLoader.IETexture).SubsetAlphaTest
            );
            SubsystemTerrain subsystemTerrain = generator.SubsystemTerrain;
            int direction = GetDirection(value);
            Point3 faceToPoint = CellFace.FaceToPoint3(direction);
            int blockValue = subsystemTerrain.Terrain.GetCellValueFast(x + faceToPoint.X, y + faceToPoint.Y, z + faceToPoint.Z);
            if (Terrain.ExtractContents(blockValue) is BaseDeviceBlock.Index) {
                DeviceBlock deviceBlock = DevicesBlockManager.GetDevice(blockValue);
                if (deviceBlock is IFluidTransferrerDevice fluidTransferrerDevice && fluidTransferrerDevice.GetConnector(subsystemTerrain, blockValue, CellFace.OppositeFace(direction), x + faceToPoint.X, y + faceToPoint.Y, z + faceToPoint.Z) != null) {
                    generator.GenerateShadedMeshVertices(
                        BlocksManager.Blocks[BaseDeviceBlock.Index],
                        x,
                        y,
                        z,
                        m_portMeshByFaces[direction],
                        Color.White,
                        null,
                        null,
                        geometry.GetGeometry(IndustrialModLoader.IETexture).SubsetAlphaTest
                    );
                }
            }
            int direction2 = CellFace.OppositeFace(direction);
            Point3 faceToPoint2 = CellFace.FaceToPoint3(direction2);
            int blockValue2 = subsystemTerrain.Terrain.GetCellValueFast(x + faceToPoint2.X, y + faceToPoint2.Y, z + faceToPoint2.Z);
            if (Terrain.ExtractContents(blockValue2) is BaseDeviceBlock.Index) {
                DeviceBlock deviceBlock = DevicesBlockManager.GetDevice(blockValue2);
                if (deviceBlock is IFluidTransferrerDevice fluidTransferrerDevice && fluidTransferrerDevice.GetConnector(subsystemTerrain, blockValue2, CellFace.OppositeFace(direction2), x + faceToPoint2.X, y + faceToPoint2.Y, z + faceToPoint2.Z) != null) {
                    generator.GenerateShadedMeshVertices(
                        BlocksManager.Blocks[BaseDeviceBlock.Index],
                        x,
                        y,
                        z,
                        m_portMeshByFaces[direction2],
                        Color.White,
                        null,
                        null,
                        geometry.GetGeometry(IndustrialModLoader.IETexture).SubsetAlphaTest
                    );
                }
            }
        }

        public override DeviceBehavior[] GetBehaviors(Project project) => [new FluidPipeDeviceBehavior(project)];

        public Type GetTransferringFluidType() => typeof(ITroughTransportableFluid);

        public HashSet<int> GetFaceMask(int value) => [GetDirection(value), CellFace.OppositeFace(GetDirection(value))];

        public PipeFluidConnection GetConnection(HashSet<FluidTransferrer> fromTransferrers, HashSet<FluidTransferrer> toTransferrers, HashSet<FluidConnector> fromConnectors, HashSet<FluidConnector> toConnectors, float rate, Type transferringType, SubsystemFluidTransfer subsystemFluidTransfer) => new CastingTroughPipeFluidConnection(fromTransferrers, toTransferrers, fromConnectors, toConnectors, rate, transferringType, subsystemFluidTransfer);

        public override int GetMaxStacking(int value) => 40;
    }
}